#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <openssl/ossl_typ.h>
extern "C" {
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
}
#include <iostream>
using namespace std;

int main()
{
    unsigned char inbuf[1024];
    unsigned char outbuf[2048];
    unsigned char inres[1024];
    for (int i = 0; i < 1023; ++i) {
        inbuf[i] = 'A' + (i % 26);
    }
    inbuf[1023] = '\0';
    memset(outbuf, '\0', 2048);
    memset(inres, '\0', 1024);
    EVP_PKEY* pkey = nullptr;
    auto keygenctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr);
    if (!keygenctx) {
        cout << "Keygen Error" << endl;
        return -1;
    }
    EVP_PKEY_keygen_init(keygenctx);
    if (EVP_PKEY_CTX_set_rsa_keygen_bits(keygenctx, 1024) <= 0) {
        cout << "bit" << endl;
        EVP_PKEY_CTX_free(keygenctx);
        return -1;
    }
    if (EVP_PKEY_CTX_set_rsa_padding(keygenctx, RSA_PKCS1_PADDING) <= 0) {
        cout << "pad" << endl;
        EVP_PKEY_CTX_free(keygenctx);
        return -1;
    }
    if (EVP_PKEY_keygen(keygenctx, &pkey) != 1) {
        cout << "Gen Error" << endl;
        return -1;
    }
    EVP_PKEY_CTX_free(keygenctx);

    auto enctx = EVP_PKEY_CTX_new(pkey, nullptr);
    EVP_PKEY_encrypt_init(enctx);

    auto* r = EVP_PKEY_get0_RSA(pkey);
    int keysize = RSA_size(r);
    int block_size = keysize - RSA_PKCS1_PADDING_SIZE;
    size_t outpointer = 0;
    size_t inpointer = 0;
    size_t restinput = 1024;
    size_t restoutput = 2048;
    while (outpointer < 2048 && inpointer < 1024) {
        size_t inputsize = (restinput) < (block_size) ? (restinput) : (block_size);
        if (EVP_PKEY_encrypt(enctx, outbuf + outpointer, &restoutput, inbuf + inpointer, inputsize) > 0) {
            outpointer += restoutput;
            inpointer += inputsize;
            restinput = 1024 - inpointer;
            restoutput = 2048 - outpointer;
        } else {
            ERR_print_errors_fp(stderr);
        }
    }
    for (int i = 0; i < outpointer; ++i) {
        cout << outbuf[i];
    }
    cout << endl;
    EVP_PKEY_CTX_free(enctx);

    auto detx = EVP_PKEY_CTX_new(pkey, nullptr);
    size_t totlen = outpointer;
    restinput = outpointer;
    inpointer = 0;
    outpointer = 0;
    restoutput = 1024;
    EVP_PKEY_decrypt_init(detx);
    while (inpointer < totlen) {
        EVP_PKEY_decrypt(detx, inres + outpointer, &restoutput, outbuf + inpointer, keysize);
        inpointer += keysize;
        outpointer += restoutput;
        restoutput = 1024 - outpointer;
        restinput -= keysize;
    }
    for (int i = 0; i < outpointer; ++i) {
        cout << inres[i];
    }
    EVP_PKEY_CTX_free(detx);
    EVP_PKEY_free(pkey);
}
